package com.lagou.config;

import com.lagou.annotation.AutoWried;
import com.lagou.annotation.Controller;
import com.lagou.annotation.RequestMapping;
import com.lagou.annotation.Service;
import com.lagou.interceptor.HandlerInterceptor;
import com.lagou.pojo.Handler;
import com.lagou.utils.PropertyUtils;
import org.apache.commons.lang.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.regex.Pattern;

import static java.util.stream.Collectors.toList;

/**
 * desc:
 *
 * @author tengfei wang
 * @version 1.0
 * @date Created in 13/9/2020
 */
public class DispatcherServlet extends HttpServlet {

    private final List<String> classNames = new ArrayList<>();

    private final Map<String, Object> beanMap = new HashMap<>();

    private final List<Handler> handlers = new ArrayList<>();

    private String configLocation;

    private List<HandlerInterceptor> handlerInterceptors = new ArrayList<>();

    @Override
    public void init(ServletConfig config) {
        configLocation = config.getInitParameter("contextConfigLocation");
        String basePackage = PropertyUtils.getProperty(configLocation, "basePackage");
        doLoadConfig(basePackage);
    }

    public void doLoadConfig(String basePackage) {
        //初始化配置
        doScan(basePackage);
        //bean注入
        doInitBean();
        //配置依赖
        doAutoWried();
        //配置处理器映射
        doHandlerMapping();
        //配置拦截器
        doHandlerInterceptor();
    }

    private void doHandlerInterceptor() {
        try {
            String interceptor = PropertyUtils.getProperty(configLocation, "interceptor");
            if (interceptor.contains(",")) {
                String[] split = interceptor.split(",");
                for (String s : split) {
                    Class<?> aClass = Class.forName(interceptor);
                    handlerInterceptors.add((HandlerInterceptor) aClass.newInstance());
                }
            } else {
                Class<?> aClass = Class.forName(interceptor);
                handlerInterceptors.add((HandlerInterceptor) aClass.newInstance());
            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private void doHandlerMapping() {
        if (beanMap.isEmpty()) {
            return;
        }
        for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
            Object clazz = entry.getValue();
            // 如果没有Controller注解不处理
            if (!clazz.getClass().isAnnotationPresent(Controller.class)) {
                continue;
            }
            // 处理controller中的handler
            String path = "";
            // 如果类有RequestMapping注解的话获取进行拼接
            if (clazz.getClass().isAnnotationPresent(RequestMapping.class)) {
                RequestMapping annotationClass = clazz.getClass().getAnnotation(RequestMapping.class);
                if (!"".equals(annotationClass.value().trim())) {
                    path = annotationClass.value();
                }
            }
            Method[] methods = clazz.getClass().getDeclaredMethods();
            // 循环遍历，找出有RequestMapping注解的方法，并放入handlers集合中
            for (Method method : methods) {
                if (!method.isAnnotationPresent(RequestMapping.class)) {
                    continue;
                }
                RequestMapping annotationMethod = method.getDeclaredAnnotation(RequestMapping.class);
                String methodPath = "";
                if (!"".equals(annotationMethod.value().trim())) {
                    methodPath = path + annotationMethod.value();
                }
                Handler handler = new Handler(method, clazz, Pattern.compile(methodPath));
                // 处理handler参数
                Parameter[] parameters = method.getParameters();
                for (int i = 0; i < parameters.length; i++) {
                    Class<?> type = parameters[i].getType();
                    // 如果参数类型是HttpServletRequest 或者 HttpServletResponse，取类名
                    if (type == HttpServletRequest.class || type == HttpServletResponse.class) {
                        handler.getParameters().put(type.getSimpleName(), i);
                    } else {
                        //普通参数的话取名称直接放入
                        handler.getParameters().put(parameters[i].getName(), i);
                    }
                }
                handlers.add(handler);
            }

        }
    }

    private void doAutoWried() {
        if (beanMap.isEmpty()) {
            return;
        }
        for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
            Object clazz = entry.getValue();
            Class<?> aClass = clazz.getClass();
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                boolean present = declaredField.isAnnotationPresent(AutoWried.class);
                //如果属性有AutoWried 注解的话属性注入
                if (present) {
                    declaredField.setAccessible(true);
                    //获取注入的属性
                    Object o = beanMap.get(declaredField.getType().getSimpleName());
                    try {
                        // 注入
                        declaredField.set(clazz, o);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    //重新放入beanMap中
                    beanMap.put(entry.getKey(), clazz);
                }
            }
        }
    }

    private void doInitBean() {
        if (classNames.size() <= 0) { return; }
        try {
            for (String classFile : classNames) {
                Class<?> aClass = Class.forName(classFile);
                // 处理Controller
                boolean present = aClass.isAnnotationPresent(Controller.class);
                if (present) {
                    String name = aClass.getSimpleName();
                    beanMap.put(name, aClass.newInstance());
                }
                // 处理Service
                boolean serviceIsPresent = aClass.isAnnotationPresent(Service.class);
                if (serviceIsPresent) {
                    Object newInstance = aClass.newInstance();
                    String simpleName = aClass.getSimpleName();
                    Class<?>[] interfaces = aClass.getInterfaces();
                    // 是否实现接口
                    // 如果没有实现接口，看Service注解中是否有值，有的话取出来，没有的话使用类名
                    if (interfaces.length <= 0) {
                        Service annotation = aClass.getAnnotation(Service.class);
                        if ("".equals(annotation.value())) {
                            beanMap.put(lowerFirst(simpleName), newInstance);
                        } else {
                            beanMap.put(annotation.value(), newInstance);
                        }
                    } else {
                        //实现接口的取接口的名称并放入beanMap中
                        for (Class<?> anInterface : interfaces) {
                            beanMap.put(anInterface.getSimpleName(), newInstance);
                        }
                    }
                }
            }
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 首字符小写
     *
     * @param name 参数名
     * @return 首字母小写之后的参数
     */
    private String lowerFirst(String name) {
        char[] chars = name.toCharArray();
        if (chars[0] > 'A' && chars[0] < 'Z') {
            chars[0] += 32;
        }
        return Arrays.toString(chars);
    }

    private void doScan(String basePackage) {
        //获取指定包名的路径
        String path = Thread.currentThread().getContextClassLoader().getResource("").getPath() + basePackage.replaceAll("\\.", "/");

        File pack = new File(path);
        File[] files = pack.listFiles();
        if (files == null || files.length == 0) {
            return;
        }
        //遍历所有的包以及包下的所有class文件，并放入集合classNames中
        for (File file : files) {
            //如果是文件夹，递归
            if (file.isDirectory()) {
                doScan(basePackage + "." + file.getName());
            } else if (file.getName().endsWith(".class")) {
                String className = basePackage + "." + file.getName().replace(".class", "");
                classNames.add(className);
            }
        }
    }


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String uri = req.getRequestURI();
        // 找出来匹配的路径
        List<Handler> collect = handlers.stream().filter(w -> w.getUrlPath().matcher(uri).matches()).collect(toList());
        // 如果不匹配，返回404
        if (collect.size() <= 0) {
            resp.getWriter().println("404 not found.");
            return;
        }
        Handler handler = collect.get(0);
        Map<String, Integer> parameters = handler.getParameters();
        Object[] args = new Object[parameters.size()];
        // 参数设置
        for (Map.Entry<String, Integer> entry : parameters.entrySet()) {
            String parameterName = entry.getKey();
            Integer index = entry.getValue();
            if ("HttpServletRequest".equals(parameterName)) {
                args[index] = req;
                continue;
            }
            if ("HttpServletResponse".equals(parameterName)) {
                args[index] = resp;
                continue;
            }
            args[index] = req.getParameter(parameterName);
        }
        //执行方法之前先查看是否配置拦截器，如果有，执行
        if (handlerInterceptors.size() > 0) {
            for (HandlerInterceptor handlerInterceptor : handlerInterceptors) {
                try {
                    handlerInterceptor.preHandle(req, resp, handler);
                } catch (Exception e) {
                    //如果有异常，返回给用户
                    resp.getWriter().println(e.getMessage());
                    return;
                }
            }
        }
        try {
            // 反射，找到对应的方法并执行
            handler.getMethod().invoke(handler.getController(), args);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
